#include "mat4.h"
#include "dmat4.h"

namespace libvec
{
  dmat4::dmat4()
  {
    dvec4 v(0);

    this->v1 = v;
    this->v2 = v;
    this->v3 = v;
    this->v4 = v;
  }

  dmat4::dmat4(mat4 m)
  {
    this->v1 = m.v1;
    this->v2 = m.v2;
    this->v3 = m.v3;
    this->v4 = m.v4;
  }

  dmat4::dmat4(double value)
  {
    this->v1.x = value;
    this->v2.y = value;
    this->v3.z = value;
    this->v4.w = value;
  }

  dmat4::dmat4(double n11, double n12, double n13, double n14,
      double n21, double n22, double n23, double n24,
      double n31, double n32, double n33, double n34,
      double n41, double n42, double n43, double n44)
  {
    this->v1.x = n11;
    this->v2.x = n12;
    this->v3.x = n13;
    this->v4.x = n14;

    this->v1.y = n21;
    this->v2.y = n22;
    this->v3.y = n23;
    this->v4.y = n24;

    this->v1.z = n31;
    this->v2.z = n32;
    this->v3.z = n33;
    this->v4.z = n34;

    this->v1.w = n41;
    this->v2.w = n42;
    this->v3.w = n43;
    this->v4.w = n44;
  }

  dmat4::dmat4(dvec4 v1, dvec4 v2, dvec4 v3, dvec4 v4)
  {
    this->v1 = v1;
    this->v2 = v2;
    this->v3 = v3;
    this->v4 = v4;
  }

  dvec4 &dmat4::operator[](int index)
  {
    static dvec4 empty;

    switch (index)
    {
      case 0:
        return this->v1;

      case 1:
        return this->v2;

      case 2:
        return this->v3;

      case 3:
        return this->v4;
    }

    return empty;
  }

  dmat4 dmat4::operator=(dmat4 m)
  {
    this->v1 = m.v1;
    this->v2 = m.v2;
    this->v3 = m.v3;
    this->v4 = m.v4;

    return *this;
  }

  bool dmat4::operator==(dmat4 m)
  {
    return (this->v1 == m.v1) && (this->v2 == m.v2) && (this->v3 == m.v3) &&
      (this->v4 == m.v4);
  }

  bool dmat4::operator!=(dmat4 m)
  {
    return (this->v1 != m.v1) || (this->v2 != m.v2) || (this->v4 != m.v4) ||
      (this->v4 != m.v4);
  }

  dmat4 &dmat4::operator+()
  {
    return *this;
  }

  dmat4 &dmat4::operator-()
  {
    static dmat4 out;

    out.v1 = -this->v1;
    out.v2 = -this->v2;
    out.v3 = -this->v3;
    out.v4 = -this->v4;

    return out;
  }

  dmat4 dmat4::operator+(dmat4 m)
  {
    static dmat4 out;

    out.v1 = this->v1 + m.v1;
    out.v2 = this->v2 + m.v2;
    out.v3 = this->v3 + m.v3;
    out.v4 = this->v4 + m.v4;

    return out;
  }

  dmat4 dmat4::operator+=(dmat4 m)
  {
    this->v1 += m.v1;
    this->v2 += m.v2;
    this->v3 += m.v3;
    this->v4 += m.v4;

    return *this;
  }

  dmat4 dmat4::operator-(dmat4 m)
  {
    static dmat4 out;

    out.v1 = this->v1 - m.v1;
    out.v2 = this->v2 - m.v2;
    out.v3 = this->v3 - m.v3;
    out.v4 = this->v4 - m.v4;

    return out;
  }

  dmat4 dmat4::operator-=(dmat4 m)
  {
    this->v1 -= m.v1;
    this->v2 -= m.v2;
    this->v3 -= m.v3;
    this->v4 -= m.v4;

    return *this;
  }

  dvec4 dmat4::operator*(dvec4 v)
  {
    static dvec4 out;

    out.x = this->v1.x*v.x + this->v2.x*v.y + this->v3.x*v.z + this->v4.x*v.w;
    out.y = this->v1.y*v.x + this->v2.y*v.y + this->v3.y*v.z + this->v4.y*v.w;
    out.z = this->v1.z*v.x + this->v2.z*v.y + this->v3.z*v.z + this->v4.z*v.w;
    out.w = this->v1.w*v.x + this->v2.w*v.y + this->v3.w*v.z + this->v4.w*v.w;

    return out;
  }

  dmat4 dmat4::operator*(dmat4 m)
  {
    static dmat4 out;

    out.v1.x = this->v1.x*m.v1.x + this->v2.x*m.v1.y + this->v3.x*m.v1.z + this->v4.x*m.v1.w;
    out.v2.x = this->v1.x*m.v2.x + this->v2.x*m.v2.y + this->v3.x*m.v2.z + this->v4.x*m.v2.w;
    out.v3.x = this->v1.x*m.v3.x + this->v2.x*m.v3.y + this->v3.x*m.v3.z + this->v4.x*m.v3.w;
    out.v4.x = this->v1.x*m.v4.x + this->v2.x*m.v4.y + this->v3.x*m.v4.z + this->v4.x*m.v4.w;

    out.v1.y = this->v1.y*m.v1.x + this->v2.y*m.v1.y + this->v3.y*m.v1.z + this->v4.y*m.v1.w;
    out.v2.y = this->v1.y*m.v2.x + this->v2.y*m.v2.y + this->v3.y*m.v2.z + this->v4.y*m.v2.w;
    out.v3.y = this->v1.y*m.v3.x + this->v2.y*m.v3.y + this->v3.y*m.v3.z + this->v4.y*m.v3.w;
    out.v4.y = this->v1.y*m.v4.x + this->v2.y*m.v4.y + this->v3.y*m.v4.z + this->v4.y*m.v4.w;

    out.v1.z = this->v1.z*m.v1.x + this->v2.z*m.v1.y + this->v3.z*m.v1.z + this->v4.z*m.v1.w;
    out.v2.z = this->v1.z*m.v2.x + this->v2.z*m.v2.y + this->v3.z*m.v2.z + this->v4.z*m.v2.w;
    out.v3.z = this->v1.z*m.v3.x + this->v2.z*m.v3.y + this->v3.z*m.v3.z + this->v4.z*m.v3.w;
    out.v4.z = this->v1.z*m.v4.x + this->v2.z*m.v4.y + this->v3.z*m.v4.z + this->v4.z*m.v4.w;

    out.v1.w = this->v1.w*m.v1.x + this->v2.w*m.v1.y + this->v3.w*m.v1.z + this->v4.w*m.v1.w;
    out.v2.w = this->v1.w*m.v2.x + this->v2.w*m.v2.y + this->v3.w*m.v2.z + this->v4.w*m.v2.w;
    out.v3.w = this->v1.w*m.v3.x + this->v2.w*m.v3.y + this->v3.w*m.v3.z + this->v4.w*m.v3.w;
    out.v4.w = this->v1.w*m.v4.x + this->v2.w*m.v4.y + this->v3.w*m.v4.z + this->v4.w*m.v4.w;

    return out;
  }

  dmat4 dmat4::operator*=(dmat4 m)
  {
    static dmat4 out;

    out.v1.x = this->v1.x*m.v1.x + this->v2.x*m.v1.y + this->v3.x*m.v1.z + this->v4.x*m.v1.w;
    out.v2.x = this->v1.x*m.v2.x + this->v2.x*m.v2.y + this->v3.x*m.v2.z + this->v4.x*m.v2.w;
    out.v3.x = this->v1.x*m.v3.x + this->v2.x*m.v3.y + this->v3.x*m.v3.z + this->v4.x*m.v3.w;
    out.v4.x = this->v1.x*m.v4.x + this->v2.x*m.v4.y + this->v3.x*m.v4.z + this->v4.x*m.v4.w;

    out.v1.y = this->v1.y*m.v1.x + this->v2.y*m.v1.y + this->v3.y*m.v1.z + this->v4.y*m.v1.w;
    out.v2.y = this->v1.y*m.v2.x + this->v2.y*m.v2.y + this->v3.y*m.v2.z + this->v4.y*m.v2.w;
    out.v3.y = this->v1.y*m.v3.x + this->v2.y*m.v3.y + this->v3.y*m.v3.z + this->v4.y*m.v3.w;
    out.v4.y = this->v1.y*m.v4.x + this->v2.y*m.v4.y + this->v3.y*m.v4.z + this->v4.y*m.v4.w;

    out.v1.z = this->v1.z*m.v1.x + this->v2.z*m.v1.y + this->v3.z*m.v1.z + this->v4.z*m.v1.w;
    out.v2.z = this->v1.z*m.v2.x + this->v2.z*m.v2.y + this->v3.z*m.v2.z + this->v4.z*m.v2.w;
    out.v3.z = this->v1.z*m.v3.x + this->v2.z*m.v3.y + this->v3.z*m.v3.z + this->v4.z*m.v3.w;
    out.v4.z = this->v1.z*m.v4.x + this->v2.z*m.v4.y + this->v3.z*m.v4.z + this->v4.z*m.v4.w;

    out.v1.w = this->v1.w*m.v1.x + this->v2.w*m.v1.y + this->v3.w*m.v1.z + this->v4.w*m.v1.w;
    out.v2.w = this->v1.w*m.v2.x + this->v2.w*m.v2.y + this->v3.w*m.v2.z + this->v4.w*m.v2.w;
    out.v3.w = this->v1.w*m.v3.x + this->v2.w*m.v3.y + this->v3.w*m.v3.z + this->v4.w*m.v3.w;
    out.v4.w = this->v1.w*m.v4.x + this->v2.w*m.v4.y + this->v3.w*m.v4.z + this->v4.w*m.v4.w;

    this->v1 = out.v1;
    this->v2 = out.v2;
    this->v3 = out.v3;
    this->v4 = out.v4;

    return out;
  }

  dmat4 dmat4::operator*(double scale)
  {
    static dmat4 out;

    out.v1 = this->v1 * scale;
    out.v2 = this->v2 * scale;
    out.v3 = this->v3 * scale;
    out.v4 = this->v4 * scale;

    return out;
  }

  dmat4 dmat4::operator*=(double scale)
  {
    this->v1 *= scale;
    this->v2 *= scale;
    this->v3 *= scale;
    this->v4 *= scale;

    return *this;
  }

  dmat4 dmat4::operator/(double scale)
  {
    static dmat4 out;

    out.v1 = this->v1 / scale;
    out.v2 = this->v2 / scale;
    out.v3 = this->v3 / scale;
    out.v4 = this->v4 / scale;

    return out;
  }

  dmat4 dmat4::operator/=(double scale)
  {
    this->v1 /= scale;
    this->v2 /= scale;
    this->v3 /= scale;
    this->v4 /= scale;

    return *this;
  }
}

